package allod.concurrent

import scala.actors.Actor
import scala.actors.Actor._
import java.awt.image.BufferedImage
import collection.mutable

/**
 * Created by IntelliJ IDEA.
 * User: allod
 * Date: 6 трав 2010
 * Time: 21:24:43
 */


case class Request(sender: Actor, original: BufferedImage, input: BufferedImage, action: BufferedImage => BufferedImage)
case class Response(sender: Actor, original: BufferedImage, result: BufferedImage)

case class Task(index: Int, input: BufferedImage, action: BufferedImage => BufferedImage)
case class Result(index: Int, result: BufferedImage)

case class Ready(sender: Actor)
case object Stop

class WorkerPool(val dest: Actor, val poolSize: Int) {
  private var parts: mutable.Map[Int, BufferedImage] = mutable.Map.empty[Int, BufferedImage]
  private var originalImage: BufferedImage = null
  private var isBusy = false

  private val consumers = List() ++ (for (n <- 0 until poolSize) yield consumer(n))

  val coordinator = actor {
    loop {
      react {
        case Request(sender, original, input, action) =>
          if (!isBusy) {
            isBusy = true
            originalImage = original
            val parts = splitImage(input, poolSize)
            val tasks = for (i <- 0 until poolSize) yield (i, parts(i))
            for ((index, img) <- tasks) {
              consumers(index) ! Task(index, img, action)
            }
          } 
        case Result(id, img) =>
          parts += id -> img
          if (parts.size == poolSize) {
            val result = recomposeImage(getOrderedList(parts))
            dest ! Response(self, originalImage, result)
            parts = mutable.Map.empty[Int, BufferedImage]
            isBusy = false
          }
        case Stop =>
          consumers foreach {_ ! Stop}
          exit
      }
    }
  }

  private def consumer(n: Int): Actor = actor {
      loop {
        react {
          case Ready(sender) =>
            sender ! Ready(self)
          case Task(index, img, action) =>
            coordinator ! Result(index, action(img))
          case Stop => exit
        }
      }
    }
  
  private def recomposeImage(parts: List[BufferedImage]): BufferedImage = {
    val partWidth = parts.head.getWidth()
    val width = originalImage.getWidth
    val height = originalImage.getHeight
    val resType = originalImage.getType
    val result = new BufferedImage(width, height, resType)
    for (i <- 0 until parts.size) {
      result.getGraphics.drawImage(parts(i), i * partWidth, 0, null)
    }
    result
  }

  private def splitImage(image: BufferedImage, n: Int): List[BufferedImage] = {
    val newWidth = image.getWidth / n
    val result = for (i <- 0 until n) yield {
      val img = new BufferedImage(newWidth, image.getHeight, image.getType)
      img.getGraphics.drawImage(image, 0, 0, newWidth, image.getHeight,
        i * newWidth, 0, (i + 1) * newWidth, image.getHeight, null)
      img
    }
    result.toList
  }

  private def getOrderedList(source: mutable.Map[Int, BufferedImage]) = {
    val orderedKeys = source.keySet.toList.sort((x: Int, y: Int) => x < y)
    val result = for (key <- orderedKeys) yield source(key)
    result.toList
  }
}